package com.csw.shuanfa.CodeImprove.redlockThread;

import com.csw.shuanfa.SuanFaApplication;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.test.context.junit4.SpringRunner;

import java.time.Duration;
import java.util.Collections;
import java.util.List;


@SpringBootTest(classes = SuanFaApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class RedisTransaction {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private String key = "key";
    private String value = "value";
    private int minutes = 1;

    
    @Test
    public void test() {
        //set-key 非原子性
        stringRedisTemplate.opsForValue().set(key + "#1", value);
        stringRedisTemplate.expire(key + "#1", Duration.ofMinutes(minutes));
        //set-key 原子性【【推荐】】
        stringRedisTemplate.opsForValue().set(key + "#2", value, Duration.ofMinutes(minutes));
        //set-key 原子性-用于分布式锁【【推荐】】
        stringRedisTemplate.opsForValue().setIfAbsent(key + "#3", value, Duration.ofMinutes(minutes));

        //increment 非原子性
        stringRedisTemplate.opsForValue().increment(key + "#4");
        stringRedisTemplate.expire(key + "#4", Duration.ofMinutes(minutes));

        //increment-原子性-lua【设置自增，失效时间一分钟，只有第一次才会更新时间】
        String luaScript = "local count = redis.call('incr', KEYS[1]); if count == 1 then redis.call('expire', KEYS[1], ARGV[1]) end; return count;";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key + "#5"), String.valueOf(60));

        //原子性-非批量【【推荐】】替换lua###################################################################################################
        Long st6 = System.currentTimeMillis();

        List<Object> executeRes = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
            @Override
            public <K, V> List<Object> execute(RedisOperations<K, V> operations) {
                try {
                    //开启原子性
                    operations.multi();
                    //多个非原子性操作[没有返回之前拿不到值]
                    Long increment = operations.opsForValue().increment((K) (key + "#6"));
                    operations.expire((K) (key + "#6"), Duration.ofHours(minutes));
//                    if (true) {
//                        throw new RuntimeException();
//                    }
                    //事务返回结果[外层取返回的顺序]
                    return operations.exec();
                } catch (Exception e) {
                    // 捕获到异常后手动回滚事务
                    operations.discard();
                    throw e;
                }
            }
        });
        System.out.println(executeRes.get(0));
        log.info("耗时execute用事务(ms)：{}", (System.currentTimeMillis() - st6));


        //非原子性-批量【【推荐】】性能高###################################################################################################
        Long st9 = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            stringRedisTemplate.executePipelined(new SessionCallback<List<Object>>() {
                @Override
                public <K, V> List<Object> execute(RedisOperations<K, V> operations) {
                    // 以下是放入事务队列中的多个操作
                    for (int i = 0; i < 100; i++) {
                        //多个非原子性操作
                        operations.opsForValue().increment((K) (key + "#9"));
                        operations.expire((K) (key + "#9"), Duration.ofMinutes(minutes));
//                        if (true) {
//                            throw new RuntimeException();
//                        }
                    }
                    return null;
                }

            });
        }
        log.info("耗时executePipelined不用事务(ms)：{}", (System.currentTimeMillis() - st9));


    }


}
